home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 5 / SOUND_KI / SOUNDKIT.C < prev   
Text File  |  1991-10-04  |  15KB  |  618 lines

  1. /*
  2. >>    SoundKit.c
  3. >>    ⌐1990, Juri Munkki
  4. */
  5.  
  6. #define    MAKE_SOUNDs
  7. #define    SOUNDKIT_MAIN
  8. #include <Sound.h>
  9. #include <Retrace.h>
  10. #include "Shuddup.h"
  11. #include <GestaltEqu.h>
  12.  
  13. SndDoubleBufferHeader    SKDouble;
  14. SndChannelPtr            SKChannel;
  15.  
  16. #define    SKBUFFERSIZE    (1024)    /*    Size of double buffering data area.        */
  17. #define    SKBUFFERBYTES    (1024)    /*    Actual amount of sound data in buffer.    */
  18. #define    KHZ11            (0x2B7745D1L)
  19. #define    KHZ22            (0x56EE8BA3L)
  20.  
  21. int            OldVol;                /*    Sound volume at InitSoundKit time.    */
  22. int            NumSounds;            /*    Number of packed sounds.            */
  23. int            OldSound=0;            /*    Change to 1 to use old sound driver.*/
  24.  
  25. /*    Note: OldSound works only if you change it before InitSoundKit.        */
  26.  
  27. Ptr            SKPtr;                /*    Pointer for Sound Kit data.            */
  28. SoundStuff    *Sounds;            /*    Storage for sound info table.        */
  29. FFSynthPtr    SoundBuf;            /*    Our one and only FFSynthRec            */
  30. VVars        Vv;                    /*    Vertical blanking variables            */
  31. long        SKTicks;            /*    Tickcount from Sound Kit VBL.        */
  32.  
  33. /*
  34. >>    If A5 points to a VVars structure, the following macro will become
  35. >>    xx(A5), where xx is the offset into that structure element.
  36. */
  37. #define VBV(field)        ((int) &((VVars *) 0)->field)(A5)
  38.  
  39. /*
  40. >>    SKWorkHorse is used by the SoundManager compatible routines to
  41. >>    fill up a new buffer of 512 sound bytes. It mixes two channels
  42. >>    into one by adding them up.
  43. */
  44. void    SKWorkHorse()
  45. {
  46. asm    {
  47.         
  48.         move.l    VBV(SoundB),A0            ;    Get address of sound buffer
  49.  
  50.         move.w    VBV(CountA),D0            ;    Is channel A active?
  51.         beq.s    @silentone                ;    No
  52.                                         ;    Yes:
  53.         sub.w    #1,VBV(CountA)            ;    Decrement counter
  54.         move.l    VBV(ChanA),A1            ;    Get sound data address
  55.  
  56.         move.w    VBV(CountB),D0            ;    Is channel B active?
  57.         bne.s    @twosounds                ;    Yes
  58.                                         ;    No:
  59.         move.l    #0x40404040,D1            ;    Other channel is silent
  60.     
  61.         moveq.l    #63,D0                    ;    Loop counter.
  62. @vbAloop
  63.         move.l    D1,D2                    ;    Copy silence
  64.         add.l    (A1)+,D2                ;    Add sound to silence
  65.         move.l    D2,(A0)+                ;    Store sound+silence
  66.  
  67.         move.l    D1,D2                    ;    ...
  68.         add.l    (A1)+,D2
  69.         move.l    D2,(A0)+
  70.  
  71.         dbra    D0,@vbAloop
  72.         move.l    A1,VBV(ChanA)            ;    Update sound data pointer
  73.         return                            ;    Return from vbl routine
  74.  
  75. @silentone
  76.         move.w    VBV(CountB),D0            ;    Are both channels silent?
  77.         beq.s    @nosound                ;    Yes
  78.                                         ;    No
  79.         sub.w    #1,VBV(CountB)            ;    Play only channel B.
  80.         move.l    VBV(ChanB),A1            ;    comments same as for
  81.         move.l    #0x40404040,D1            ;    channel A above.
  82.         moveq.l    #63,D0
  83.  
  84. @vbBloop
  85.         move.l    D1,D2
  86.         add.l    (A1)+,D2
  87.         move.l    D2,(A0)+
  88.  
  89.         move.l    D1,D2
  90.         add.l    (A1)+,D2
  91.         move.l    D2,(A0)+
  92.         dbra    D0,@vbBloop
  93.  
  94.         move.l    A1,VBV(ChanB)
  95.         return                            ;    Return from vbl routine
  96. @twosounds
  97.         sub.w    #1,VBV(CountB)            ;    Decrement channel B count
  98.         move.l    VBV(ChanB),A2            ;    A2 = channel B sound data pointer
  99.         move.w    #63,D0                    ;    
  100.  
  101. @vbABloop
  102.         move.l    (A1)+,D1                ;    Get Sound channel A
  103.         add.l    (A2)+,D1                ;    Add channel B
  104.         move.l    D1,(A0)+                ;    Store sound
  105.  
  106.         move.l    (A1)+,D1                ;    Get Sound channel A
  107.         add.l    (A2)+,D1                ;    Add channel B
  108.         move.l    D1,(A0)+                ;    Store sound
  109.  
  110.         dbra    D0,@vbABloop            ;    Loop
  111.         move.l    A1,VBV(ChanA)            ;    Update sound channel A data pointer
  112.         move.l    A2,VBV(ChanB)            ;    Update sound channel B data pointer
  113.         return                            ;    Return from vbl routine
  114. @nosound
  115.         move.l    #0x80808080,D1            ;    Silence
  116.         move.w    #127,D0                    ;    512 bytes of silence.
  117. @silencer
  118.         move.l    D1,(A0)+                ;    Store silence
  119.         dbra    D0,@silencer            ;    loop
  120. @endvbl
  121.         return                            ;    Return from vbl routine
  122.     }
  123. }
  124.  
  125. /*
  126. >>    See IM-VI to find out what a DoubleBackRoutine is.
  127. >>    Basically it's just a routine that feeds data to the
  128. >>    sound manager.
  129. */
  130. pascal    void    SKDoubleBackRoutine(chan,buf)
  131. SndChannelPtr    chan;
  132. SndDoubleBuffer    *buf;
  133. {
  134.     register    char    *sdata;
  135.     register    VVars    *VVFrame;
  136.                 char    sounder[512];
  137.     
  138.     buf->dbFlags=dbBufferReady;
  139.     
  140.     VVFrame=(VVars *)buf->dbUserInfo[0];
  141.     VVFrame->SoundB=sounder;
  142.  
  143.     sdata=buf->dbSoundData;
  144.  
  145. asm    {    move.l    A5,-(sp)
  146.         move.l    VVFrame,A5
  147.         bsr        SKWorkHorse
  148.         move.l    (sp)+,A5
  149.  
  150.         lea        sounder,A0
  151.         moveq.l    #127,D0
  152. @loop
  153.         move.b    (A0)+,D1
  154.         move.b    D1,(sdata)+
  155.         move.b    D1,(sdata)+
  156.  
  157.         move.b    (A0)+,D1
  158.         move.b    D1,(sdata)+
  159.         move.b    D1,(sdata)+
  160.  
  161.         move.b    (A0)+,D1
  162.         move.b    D1,(sdata)+
  163.         move.b    D1,(sdata)+
  164.         
  165.         move.b    (A0)+,D1
  166.         move.b    D1,(sdata)+
  167.         move.b    D1,(sdata)+
  168.         
  169.         dbra    D0,@loop
  170.     }
  171. }
  172.  
  173. /*
  174. >>    Setup vertical blanking variables &
  175. >>    install vertical blanking task.
  176. */
  177. void    InstallMyVBL()
  178. {
  179.     register    long    vbltask;
  180.     register    Handle     SystemHand;
  181.     
  182.     asm    {
  183.         lea        @myvbltask,A0        ;    Get address of vbltask
  184.         move.l    A0,vbltask            ;    Store in local variable
  185.         lea        @mybase,A0            ;    Get addr of variable base storage
  186.         lea        Vv,A1                ;    Get addr of Vv record
  187.         move.l    A1,(A0)                ;    Store base in base storage
  188.         }
  189.     
  190.     SystemHand=GetResource(SKRESTYPE,SKSYSJUMP);
  191.     Vv.VBL.qType=vType;                /*    Vertical blanking queue.    */
  192.     Vv.VBL.vblAddr=(ProcPtr) *SystemHand;
  193.     *(long *)(2+*SystemHand)=(long)vbltask;
  194.                                     /*    Address of task                */
  195.     Vv.VBL.vblCount=1;                /*    Every 1/60 seconds            */
  196.     Vv.VBL.vblPhase=0;                /*    0 is ok..                    */
  197.     Vv.TickPtr=&SKTicks;            /*    Faster than TickCount()        */
  198.     VInstall((void *)&Vv.VBL);        /*    Install task in queue        */
  199.     if(0)
  200. asm    {    
  201. @myvbltask
  202.         move.l    A5,-(SP)                ;    Save A5
  203.         move.l    @mybase,A5                ;    Get Vv address into A5
  204.         move.w    #1,VBV(VBL.vblCount)    ;    Call again in 1/60 seconds
  205.         move.l    VBV(TickPtr),A0            ;    Get pointer to private tickcount
  206.         addq.l    #1,(A0)                    ;    Increment private tickcount
  207.  
  208.         clr.l    D0                        ;    Change sound buffer by
  209.         move.w    #256,D0                    ;    setting:
  210.         sub.w    VBV(SoundP),D0            ;    SoundP = 256 - SoundP
  211.         move.w    D0,VBV(SoundP)            ;    Store SoundP for next time
  212.         move.l    D0,VBV(SPar.ioActCount)    ;    Store SoundP into ioActCount
  213.         move.l    VBV(SoundB),A0            ;    Get address of sound buffer
  214.         add.l    D0,A0                    ;    Add SoundP to SoundB
  215.  
  216.         move.w    VBV(CountA),D0            ;    Is channel A active?
  217.         beq.s    @silentone                ;    No
  218.                                         ;    Yes:
  219.         sub.w    #1,VBV(CountA)            ;    Decrement counter
  220.         move.l    VBV(ChanA),A1            ;    Get sound data address
  221.  
  222.         move.w    VBV(CountB),D0            ;    Is channel B active?
  223.         bne.s    @twosounds                ;    Yes
  224.                                         ;    No:
  225.         move.l    #0x40404040,D1            ;    Other channel is silent
  226.         move.l    D1,D2                    ;    Copy silence
  227.         add.l    (A1)+,D2                ;    Add sound to silence
  228.         move.l    D2,(A0)+                ;    Store sound+silence
  229.     
  230.         moveq.l    #22,D0                    ;    Loop counter.
  231. @vbAloop
  232.         move.l    D1,D2                    ;    Copy silence
  233.         add.l    (A1)+,D2                ;    Add sound to silence
  234.         move.l    D2,(A0)+                ;    Store sound+silence
  235.  
  236.         move.l    D1,D2                    ;    ...
  237.         add.l    (A1)+,D2
  238.         move.l    D2,(A0)+
  239.  
  240.         dbra    D0,@vbAloop
  241.         move.l    A1,VBV(ChanA)            ;    Update sound data pointer
  242.         move.l    (SP)+,A5                ;    Restore A5
  243.         rts                                ;    Return from vbl routine
  244.  
  245. @silentone
  246.         move.w    VBV(CountB),D0            ;    Are both channels silent?
  247.         beq.s    @nosound                ;    Yes
  248.                                         ;    No
  249.         sub.w    #1,VBV(CountB)            ;    Play only channel B.
  250.         move.l    VBV(ChanB),A1            ;    comments same as for
  251.         move.l    #0x40404040,D1            ;    channel A above.
  252.         moveq.l    #22,D0
  253.  
  254.         move.l    D1,D2
  255.         add.l    (A1)+,D2
  256.         move.l    D2,(A0)+
  257. @vbBloop
  258.         move.l    D1,D2
  259.         add.l    (A1)+,D2
  260.         move.l    D2,(A0)+
  261.  
  262.         move.l    D1,D2
  263.         add.l    (A1)+,D2
  264.         move.l    D2,(A0)+
  265.         dbra    D0,@vbBloop
  266.  
  267.         move.l    A1,VBV(ChanB)
  268.         move.l    (SP)+,A5                ;    Restore A5
  269.         rts                                ;    Return from vbl routine
  270. @twosounds
  271.         sub.w    #1,VBV(CountB)            ;    Decrement channel B count
  272.         move.l    VBV(ChanB),A2            ;    A2 = channel B sound data pointer
  273.         move.w    #22,D0                    ;    ((23 * 8) + 4) bytes = 188 bytes
  274.  
  275.         move.l    (A1)+,D1                ;    Get Sound channel A
  276.         add.l    (A2)+,D1                ;    Add channel B
  277.         move.l    D1,(A0)+                ;    Store sound
  278.  
  279. @vbABloop
  280.         move.l    (A1)+,D1                ;    Get Sound channel A
  281.         add.l    (A2)+,D1                ;    Add channel B
  282.         move.l    D1,(A0)+                ;    Store sound
  283.  
  284.         move.l    (A1)+,D1                ;    Get Sound channel A
  285.         add.l    (A2)+,D1                ;    Add channel B
  286.         move.l    D1,(A0)+                ;    Store sound
  287.  
  288.         dbra    D0,@vbABloop            ;    Loop
  289.         move.l    A1,VBV(ChanA)            ;    Update sound channel A data pointer
  290.         move.l    A2,VBV(ChanB)            ;    Update sound channel B data pointer
  291.         move.l    (SP)+,A5                ;    Restore A5
  292.         rts                                ;    Return from vbl routine
  293. @nosound
  294.         move.l    #0x80808080,D1            ;    Silence
  295.         move.w    #46,D0                    ;    (45+1)*4 bytes of silence
  296. @silencer
  297.         move.l    D1,(A0)+                ;    Store silence
  298.         dbra    D0,@silencer            ;    loop
  299. @endvbl
  300.         move.l    (SP)+,A5                ;    Restore A5
  301.         rts                                ;    Return from vbl routine
  302. @mybase
  303.         dc.l    0                        ;    Vv record address stored here.
  304.     }
  305. }
  306.  
  307. /*
  308. >>    You should call CloseSoundKit before exiting your program.
  309. >>    The sound kit tries to make sure you do, but doing it yourself
  310. >>    will be more reliable. It should be possible to reopen the
  311. >>    sound kit after you have closed it. I haven't tried to do that.
  312. */
  313.  
  314. static    long    oldExit;
  315.  
  316. void    CloseSoundKit()
  317. {
  318.     if(Vv.SKOpen)
  319.     {
  320.         if(OldSound)
  321.         {    VRemove((void *)&Vv.VBL);
  322.             StopSound();
  323.         }
  324.         else
  325.         {    SndDisposeChannel(SKChannel,-1);
  326.         }
  327.  
  328.         SetSoundVol(OldVol);
  329.         DisposPtr(SKPtr);
  330.         NumSounds=0;
  331.         Vv.SKOpen=0;
  332.         NSetTrapAddress(oldExit,0x9F4,ToolTrap);
  333.     }
  334. }
  335.  
  336. /*
  337. >>    ExitToShell is patched to call this routine
  338. >>    to make sure that the kit is closed before the
  339. >>    program quits. Without this patch, forgetting to
  340. >>    close the sound kit and exiting the program generated
  341. >>    some interesting and exiting side-effects like
  342. >>    crashes and trashed memory locations.
  343. */
  344.  
  345. void    SoundExitToShell()
  346. {
  347.     CloseSoundKit();
  348.     asm    {    move.l    oldExit,A0
  349.             jmp        (A0)
  350.         }
  351. }
  352. /*
  353. >>    Patch exittoshell to make sure that vbl routine is removed.
  354. */
  355. void    ExitSoundPatch()
  356. {    
  357.     oldExit=NGetTrapAddress(0x9F4,ToolTrap);
  358.     NSetTrapAddress((long)SoundExitToShell,0x9F4,ToolTrap);
  359. }
  360. /*
  361. >>    Initialize FFSynthRec and start producing silence.
  362. */
  363. void    StartNoise()
  364. {
  365.     register    long    *p;
  366.     register    int        i;
  367.  
  368.     SoundBuf=(FFSynthPtr)NewPtr(700);    /*    Allocate buffer                    */
  369.     
  370.     p=(long *)SoundBuf;
  371.     for(i=700/4;i;i--)    *p++=0x80808080;/*    Store silence in buffer            */
  372.  
  373.     SoundBuf->mode=ffMode;                /*    Freeform (sampled) data            */
  374.     SoundBuf->count=FixRatio(1,2);        /*    11 Khz sampling rate            */
  375.  
  376.     Vv.SPar.ioRefNum=-4;                /*    Sound driver.                    */
  377.     Vv.SPar.ioBuffer=(Ptr)SoundBuf;        /*    FFSynthPtr stored here            */
  378.     Vv.SPar.ioReqCount=660;                /*    Play 660 bytes (At least)        */
  379.     Vv.SPar.ioCompletion=0;                /*    No ioCompletion routine.        */
  380.     Vv.SoundB=(void *)SoundBuf->waveBytes;/*    Store buffer start address        */
  381.     Vv.SoundP=0;                        /*    Start with buffer at offset 0    */
  382.  
  383.     PBWrite(&Vv.SPar,-1);                /*    Start playing                    */
  384. }
  385.  
  386. /*
  387. >>    SKVolume allows you to change the sound volume.
  388. >>    Note that all Macs are capable of 9 distinct
  389. >>    sound volumes. Volume 0 is usually understood
  390. >>    as silence, but actually it is just a low volume
  391. >>    setting. SKVolume reassigns these values so that
  392. >>    0 is really silence and 8 is the maximum volume.
  393. */
  394. void    SKVolume(vol)
  395. int        vol;
  396. {
  397.     if(vol>8) vol=8;
  398.     if(vol<0) vol=0;
  399.     Vv.volume=vol;
  400.     if(vol)
  401.     {    SetSoundVol(vol-1);
  402.         if(OldSound)
  403.             PBWrite(&Vv.SPar,-1);
  404.     }
  405.     else
  406.     {    Vv.CountA=0;
  407.         Vv.CountB=0;
  408.     }
  409. }
  410.  
  411. /*
  412. >>    Create a double[back] buffering data structure for
  413. >>    the sound manager.
  414. */
  415. SndDoubleBuffer    *SKCreateDBuffer()
  416. {
  417.     register    SndDoubleBuffer    *buf;
  418.     register    int                i;
  419.     register    char            *p;
  420.  
  421.     buf=(SndDoubleBuffer *)NewPtr(SKBUFFERSIZE+sizeof(SndDoubleBuffer));
  422.     buf->dbNumFrames=SKBUFFERBYTES;
  423.     buf->dbUserInfo[0]= (long)&Vv;
  424.     buf->dbFlags=dbBufferReady;
  425.     
  426.     p= buf->dbSoundData;
  427.     for(i=0;i<SKBUFFERSIZE;i++)
  428.     {    *p++= 128;
  429.     }
  430.     
  431.     return    buf;
  432. }
  433. void    InitSoundKit()
  434. {
  435.     int        err;
  436.     long    response;
  437.  
  438.     GetSoundVol((void *)&OldVol);
  439.     SKVolume(OldVol+1);
  440.     DeCompress();
  441.     DecodeSounds();
  442.  
  443.     ExitSoundPatch();
  444.  
  445.     if(Gestalt(gestaltSoundAttr, &response))
  446.     {    OldSound = 1;
  447.     }
  448.     else
  449.     {    if(response & (1 << (31 - gestaltSoundIOMgrPresent)))
  450.         {    OldSound = 1;
  451.         }
  452.     }
  453.  
  454.     if(OldSound)
  455.     {    StartNoise();
  456.         InstallMyVBL();
  457.     }
  458.     else
  459.     {
  460.         SKDouble.dbhNumChannels        =1;
  461.         SKDouble.dbhSampleSize        =8;
  462.         SKDouble.dbhCompressionID    =0;
  463.         SKDouble.dbhPacketSize        =0;
  464.         SKDouble.dbhSampleRate        =KHZ22;
  465.         
  466.         SKDouble.dbhBufferPtr[0]    =SKCreateDBuffer();
  467.         SKDouble.dbhBufferPtr[1]    =SKCreateDBuffer();
  468.         SKDouble.dbhDoubleBack        =(void *)SKDoubleBackRoutine;
  469.  
  470.         SKChannel=0;
  471.         err=SndNewChannel(&SKChannel,sampledSynth,4,0L);
  472.         err=SndPlayDoubleBuffer(SKChannel,&SKDouble);
  473.     }
  474.     Vv.SKOpen=-1;
  475. }
  476.  
  477. void    PlayA(num,priority)
  478. register    int        num,priority;
  479. {
  480.     if(Vv.volume)            /*    If volume is not off.                    */
  481.     if(num<NumSounds)        /*    And the sound number looks possible.    */
  482.     if(num>=0)
  483.     if(priority >= Vv.PriA || Vv.CountA==0)    /*    Can channel be used?    */
  484.     {    Vv.CountA=0;                        /*    Stop old sound.            */
  485.         Vv.ChanA=Sounds[num].Poin;            /*    Pointer to new sound.    */
  486.         Vv.CountA=Sounds[num].Count;        /*    Start new sound.        */
  487.         Vv.PriA=priority;                    /*    Store priority.            */
  488.     }
  489.     else                    /*    Try if the other channel is available.    */
  490.     {    if(Vv.CountB==0)
  491.         {    Vv.CountB=0;
  492.             Vv.ChanB=Sounds[num].Poin;
  493.             Vv.CountB=Sounds[num].Count;
  494.             Vv.PriB=0;
  495.         }
  496.     }
  497. }
  498. void    PlayB(num,priority)
  499. register    int        num,priority;
  500. {
  501.     if(Vv.volume)
  502.     if(num<NumSounds)
  503.     if(num>=0)
  504.     if(priority >= Vv.PriB || Vv.CountB==0)
  505.     {    Vv.CountB=0;
  506.         Vv.ChanB=Sounds[num].Poin;
  507.         Vv.CountB=Sounds[num].Count;
  508.         Vv.PriB=priority;
  509.     }
  510.     else
  511.     {    if(Vv.CountA==0)
  512.         {    Vv.CountA=0;
  513.             Vv.ChanA=Sounds[num].Poin;
  514.             Vv.CountA=Sounds[num].Count;
  515.             Vv.PriA=0;
  516.         }
  517.     }
  518. }
  519.  
  520. /*
  521. >>    SKProcessHandle allows normal sound sample handles
  522. >>    to be converted to a form suitable for the sound kit.
  523. */
  524. void    SKProcessHandle(thedata)
  525. Handle    thedata;
  526. {
  527.     register    unsigned    char    *source,*dest;
  528.     register    long                len,oldlen;
  529.     register    int                    counter;
  530.     register    int                    sampleframes,samplepad;
  531.     
  532.     if(OldSound)
  533.     {    sampleframes = 185;
  534.         samplepad = 188;
  535.     }
  536.     else
  537.     {    sampleframes = 512;
  538.         samplepad = 512;
  539.     }
  540.     
  541.     oldlen = GetHandleSize(thedata);
  542.     len = (oldlen + sampleframes - 1) / sampleframes * samplepad;
  543.     
  544.     SetHandleSize(thedata,len+sizeof(long));
  545.     
  546.     if(!MemErr)
  547.     {    BlockMove(*thedata,*thedata + len - oldlen + sizeof(long), oldlen);
  548.         dest = (unsigned char *)*thedata;
  549.         *(long *)dest = (oldlen + sampleframes - 1) / sampleframes;
  550.         dest += sizeof(long);
  551.         source = dest + len - oldlen;
  552.         counter = 0;
  553.         
  554.         while(oldlen--)
  555.         {    *dest++ = (*source++)>>1;
  556.             len--;
  557.             counter++;
  558.             
  559.             if(counter == sampleframes)
  560.             {    while(counter++ < samplepad)
  561.                 {    *dest++ = 64;
  562.                     len--;
  563.                 }
  564.                 counter = 0;
  565.             }
  566.         }
  567.         while(len--)
  568.         {    *dest++ = 64;
  569.         }
  570.     }
  571. }
  572.  
  573. /*
  574. >>    You can play a preprocessed handle with the
  575. >>    following two routines. Read the manual for
  576. >>    more information.
  577. */
  578. void    SKPlayHandleA(thehand,priority)
  579. register    Handle    thehand;
  580. register    int        priority;
  581. {
  582.     if(Vv.volume)
  583.     if(priority >= Vv.PriA || Vv.CountA==0)
  584.     {    Vv.CountA=0;
  585.         Vv.ChanA=*thehand+sizeof(long);
  586.         Vv.CountA=*(long *)*thehand;
  587.         Vv.PriA=priority;
  588.     }
  589.     else
  590.     {    if(Vv.CountB==0)
  591.         {    Vv.CountB=0;
  592.             Vv.ChanB=*thehand+sizeof(long);
  593.             Vv.CountB=*(long *)*thehand;
  594.             Vv.PriB=0;
  595.         }
  596.     }
  597. }
  598. void    SKPlayHandleB(thehand,priority)
  599. register    Handle    thehand;
  600. register    int        priority;
  601. {
  602.     if(Vv.volume)
  603.     if(priority >= Vv.PriB || Vv.CountB==0)
  604.     {    Vv.CountB=0;
  605.         Vv.ChanB=*thehand+sizeof(long);
  606.         Vv.CountB=*(long *)*thehand;
  607.         Vv.PriB=priority;
  608.     }
  609.     else
  610.     {    if(Vv.CountA==0)
  611.         {    Vv.CountA=0;
  612.             Vv.ChanA=*thehand+sizeof(long);
  613.             Vv.CountA=*(long *)*thehand;
  614.             Vv.PriA=0;
  615.         }
  616.     }
  617. }
  618.